package engine.operators;

import its.util.TimeUtils;

import java.awt.Color;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.LogarithmicAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.statistics.HistogramDataset;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

import engine.ObjectiveFunction;
import engine.Operator;
import engine.Population;
import engine.operators.util.ChartUtils;
import engine.operators.util.StatisticUtils;
import engine.operators.util.StatisticsBean;

public class ChartReportingMinAvgMaxOperator<T> implements Operator<T> {

	
	private static final Logger LOGGER = Logger
			.getLogger(ChartReportingMinAvgMaxOperator.class.getName());
	
	private ObjectiveFunction<T> objFunction;
	private StatisticUtils<T> statUtil;

	private XYSeries dataset_max = new XYSeries("Max");
	private XYSeries dataset_avg = new XYSeries("Avg");
	private XYSeries dataset_min = new XYSeries("Min");
	private XYSeries dataset_var = new XYSeries("Std Dev");
	private int iteration;
	private JFreeChart chart = null;
	private XYSeriesCollection dataset = null;
	private String fileName = null;

	
	private ChartReportingMinAvgMaxOperator(ObjectiveFunction<T> objFunction) {
		this.objFunction = objFunction;
		this.statUtil = new StatisticUtils<T>(objFunction);
		
		this.iteration = 0;
		
		dataset = createDataset();
		chart = createJFreeChart(dataset, false);
	}
	
	public ChartReportingMinAvgMaxOperator(
			String algorithm,
			String configFileName,
			ObjectiveFunction<T> objFunction) {
		this(objFunction);
		
		fileName = algorithm + "-" + stripFileName(configFileName);
	}

	private String stripFileName(String configFileName) {
		if (configFileName != null) {
			if (configFileName.contains(File.separator)) {
				configFileName = configFileName.substring(configFileName.lastIndexOf(File.separator)+1);
			}
			if (configFileName.contains(".")) {
				configFileName = configFileName.substring(0, configFileName.lastIndexOf("."));
			}
		} else {
			configFileName = "NO-CONFIG";
		}
		return configFileName;
	}

	private XYSeriesCollection createDataset() {
		XYSeriesCollection dataset = new XYSeriesCollection();
		
		dataset.addSeries(dataset_max);
		dataset.addSeries(dataset_avg);
		dataset.addSeries(dataset_min);
		dataset.addSeries(dataset_var);
		
		return dataset;
	}
	
	private JFreeChart createJFreeChart(
			XYDataset dataset,
			boolean logarithmic) {


		JFreeChart chart = ChartFactory.createXYLineChart(
				"Min/Avg/Max Objective function value plot",
				"Iteration",              
				"",                  
				dataset,
				PlotOrientation.VERTICAL,
				true, false, false
		);

		// get a reference to the plot for further customisation...
		final XYPlot plot = chart.getXYPlot();
		plot.setBackgroundPaint(Color.WHITE);
		plot.setRangeGridlinePaint(Color.BLACK);
		if (logarithmic) {
			plot.setRangeAxis(new LogarithmicAxis("Value"));
		}
		return chart;

	}
	
	@Override
	public Population<T> apply(Population<T> population) {
		
		iteration++;
		
		T max = statUtil.max(population.getIndividuals());
		double maxValue = objFunction.compute(max);
		
		T min = statUtil.min(population.getIndividuals());
		double minValue = objFunction.compute(min);
		
		double mean = statUtil.mean(population.getIndividuals());
		double stdDev = statUtil.stdDev(population.getIndividuals(), mean);
		
		addToDataSet(new StatisticsBean(maxValue, mean, minValue, stdDev));
		
		drawHistorgram(population.getIndividuals());
		
		return population;
	}

	private void addToDataSet(StatisticsBean populationStatistics) {
    
    dataset_max.add(iteration, populationStatistics.getMax());
		dataset_avg.add(iteration, populationStatistics.getAvg());
		dataset_min.add(iteration, populationStatistics.getMin());
		dataset_var.add(iteration, populationStatistics.getStdDev());
    
  }

	private void drawAndSave(String what, JFreeChart chart) {
		
		try {
			drawAndSaveThrowingExceptions(what, chart);
		} catch (FileNotFoundException e) {
			LOGGER.log(Level.WARNING, "Problem with finding " + what + " file.", e);
		} catch (IOException e) {
			LOGGER.log(Level.WARNING, "Problem with " + what + " file.", e);
		}
	}

	private void drawAndSaveThrowingExceptions(
			String what,
			JFreeChart chart) 
	throws FileNotFoundException, IOException
	{
		FileOutputStream outputStream = null;
		try {
			outputStream = new FileOutputStream(new File(buildFileNameFor(what)));
			ChartUtils.writeToStream(outputStream, ChartUtils.draw(chart, 800, 600));
		} finally {
			if (outputStream != null) {
				outputStream.close();
			}
		}
	}
	
	public void drawStatistics() {
		
		drawAndSave("statistics", chart);
		
//		SwingUtilities.invokeLater(new Runnable() {
//
//			@Override
//			public void run() {
//				
//				JFrame frame = new JFrame();
//				
//				frame.add(new ChartPanel(chart));
//				frame.setVisible(true);
//				frame.setSize(new Dimension(600, 400));
//				
//				frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
//			}
//			
//		});
	}
	
	private void drawHistorgram(List<T> individuals) {
		
		if (iteration % 4 == 0) {
			double[] vals = new double[individuals.size()];

			{
				int i = 0;
				for (T ind : individuals) {
					vals[i++] = objFunction.compute(ind);
				}
			}

			HistogramDataset intervalXYDataset = new HistogramDataset();

			intervalXYDataset.addSeries("Individuals", vals, 200);
			JFreeChart chart = ChartFactory.createHistogram(
					"Histogram of " + iteration + " generation",
					"Fitness values",
					"Individuals counts",
					intervalXYDataset,
					PlotOrientation.VERTICAL,
					false,
					false,
					false);

			drawAndSave("histogram", chart);
			
			// final ChartPanel chartPanel = new ChartPanel(chart);
			//
			// SwingUtilities.invokeLater(new Runnable() {
			// @Override
			// public void run() {
			// JFrame frame = new JFrame();
			// frame.setSize(new Dimension(800, 600));
			// frame.add(chartPanel);
			// frame.setVisible(true);
			// frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
			// }
			// });
			// }
		}
	}

	private String buildFileNameFor(String forWhat) {
		return System.getProperty("hostname") + "-"
				+ TimeUtils.DATE_FORMAT.format(new Date()) + fileName + "-" + forWhat
				+ "-" + iteration + ".jpg";
	}
}

